package com.uow.algdemo.animation;

import java.io.UnsupportedEncodingException;
import java.util.List;

import com.uow.algdemo.material.Point;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Color;
import android.os.Handler;
import android.os.Message;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.view.animation.Animation.AnimationListener;
import android.widget.RelativeLayout;
import android.widget.TextView;

/**
 * This class will receive data of two string and string matching algorithm animation steps, then generate animation on animation layout.
 */
public class AnimationStringMatching extends RelativeLayout implements AnimationInterface
{
	/**
     * This is handler for whole progress of animation.
     */
	private Handler handler;
	/**
     * This is current status of disks moving or not.
     */
	private boolean running = false;
	/**
     * This is current status of animation completed or not.
     */
	private boolean finished = false;
	/**
     * This is current status of pause or not.
     */
	private boolean pause = false;
	/**
     * This array contains view of string for searching.
     */
	private Point[] tString;
	/**
     * This array contains view of pattern string.
     */
	private Point[] kString;
	/**
     * This array contains view of pattern string for skip table.
     */
	private Point[] sString;
	/**
     * This array contains view of values of skip table.
     */
	private Point[] sValue;
	/**
     * This is the string of original.
     */
	private String text;
	/**
     * This is the keyword for search.
     */
	private String pattern;
	/**
     * This array contains steps of string matching animation.
     */
	private List<int[][]> steps;
    /**
     * This is the current step of animation position.
     */
	private int position = 0;
    /**
     * This is the current speed of animation.
     */
	private int speed = 10;
    /**
     * This is the size of a point or number.
     */
	private int size = 0;
    /**
     * This is the step length of move a point or number.
     */
	private int stepm = 0;
	/**
     * @param context The Context the view is running in, through which it can access the current theme, resources, etc.
	 * @param newText The string of original.
     * @param newPattern Keyword for search.
     * @param newSteps Steps of the queens problem solution animation.
     * @param skipTable Skip table for algorithms.
     * @param width Width of layout in pixel.
     * @param height Height of layout in pixel.
     * @param psize Size of point in pixel.
     * @param aid Algorithm id.
	 */
	public AnimationStringMatching(Context context, String newText, String newPattern, List<int[][]> newSteps, int[] skipTable, int width, int height, int psize, int aid)
	{
		super(context);
        text = newText;
		pattern = newPattern;
		steps = newSteps;

		size = psize;
		int sizes = psize / 2;
		stepm = size + 10;

		if(skipTable.length > 0)
		{
			sString = new Point[skipTable.length];
			sValue = new Point[skipTable.length];
			
			TextView title = new TextView(context);
			if(aid == 14)
			{
				title.setText("Hash value");
			}
			else
			{
				title.setText("Skip table");
			}
			title.setTextColor(Color.WHITE);
    		RelativeLayout.LayoutParams paramst = new RelativeLayout.LayoutParams(70,30);
    		paramst.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
    		paramst.bottomMargin = height / 2 + size * 2 * 2 + 30;
    		paramst.leftMargin = width / 2 - 35;
    		this.addView(title,paramst);

    		if(aid == 12 || aid == 13)
    		{
	    		for(int i = 0; i < skipTable.length; i++)
		        {
	    			if(i < skipTable.length - 1)
	    			{
			        	sString[i] = new Point(context, pattern.substring(i,i+1), sizes);
			    		RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(sizes, sizes);
			    		params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
			    		params.bottomMargin = height / 2 + size * 2 * 2;
			    		params.leftMargin = ((width - ((pattern.length() + 1) * (sizes + 10))) / 2) + (i * (sizes + 10));
			    		sString[i].invalidate();
			    		this.addView(sString[i],params);
	    			}
	    			
		        	sValue[i] = new Point(context, skipTable[i], sizes);
		    		RelativeLayout.LayoutParams paramsi = new RelativeLayout.LayoutParams(sizes, sizes);
		    		paramsi.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
		    		paramsi.bottomMargin = height / 2 + size * 2 * 2 - sizes - 10;
		    		paramsi.leftMargin = ((width - ((pattern.length() + 1) * (sizes + 10))) / 2) + (i * (sizes + 10));
		    		sValue[i].invalidate();
		    		this.addView(sValue[i],paramsi);
		        }
    		}
    		else if(aid == 14)
    		{
    			sString = new Point[pattern.length()];

	    		for(int i = 0; i < pattern.length(); i++)
		        {
		        	sString[i] = new Point(context, pattern.substring(i,i+1), sizes);
		    		RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(sizes, sizes);
		    		params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
		    		params.bottomMargin = height / 2 + size * 2 * 2;
		    		params.leftMargin = ((width - (pattern.length() * (sizes + 10))) / 2) + (i * (sizes + 10));
		    		sString[i].invalidate();
		    		this.addView(sString[i],params);
		        }
	        	sValue[0] = new Point(context, skipTable[0], sizes);
	    		RelativeLayout.LayoutParams paramsi = new RelativeLayout.LayoutParams((sizes + 10) * pattern.length() - 10, sizes);
	    		paramsi.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
	    		paramsi.bottomMargin = height / 2 + size * 2 * 2 - sizes - 10;
	    		paramsi.leftMargin = ((width - (pattern.length() * (sizes + 10) - 10)) / 2) - 5;
	    		sValue[0].invalidate();
	    		this.addView(sValue[0],paramsi);
    		}
    		else if(aid == 15)
    		{
    			byte[] b = new byte[256];
    			try
    			{
					b = pattern.getBytes("US-ASCII");
				}
    			catch (UnsupportedEncodingException e)
    			{
					e.printStackTrace();
				}
	    		for(int i = 0; i < pattern.length(); i++)
		        {
		        	sString[i] = new Point(context, pattern.substring(i,i+1), sizes);
		        	sValue[i] = new Point(context, skipTable[b[i]], sizes);
		    		RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(sizes, sizes);
		    		params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
		    		params.bottomMargin = height / 2 + size * 2 * 2;
		    		params.leftMargin = ((width - (pattern.length() * (sizes + 10))) / 2) + (i * (sizes + 10));
		    		sString[i].invalidate();
		    		this.addView(sString[i],params);
	
		    		RelativeLayout.LayoutParams paramsi = new RelativeLayout.LayoutParams(sizes, sizes);
		    		paramsi.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
		    		paramsi.bottomMargin = height / 2 + size * 2 * 2 - sizes - 10;
		    		paramsi.leftMargin = ((width - (pattern.length() * (sizes + 10))) / 2) + (i * (sizes + 10));
		    		sValue[i].invalidate();
		    		this.addView(sValue[i],paramsi);
		        }
    		}
		}
		tString = new Point[text.length()];
        for(int i = 0; i < text.length(); i++)
        {
        	tString[i] = new Point(context, text.substring(i,i+1), size);
    		RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(size, size);
    		params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
    		params.bottomMargin = height / 2 + size / 2;
    		params.leftMargin = ((width - (text.length() * (size + 10))) / 2) + (i * (size + 10));
    		tString[i].invalidate();
    		this.addView(tString[i],params);
        }
		kString = new Point[pattern.length()];
        for(int i = 0; i < pattern.length(); i++)
        {
        	kString[i] = new Point(context, pattern.substring(i,i+1), size);
    		RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(size, size);
    		params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
    		params.bottomMargin = height / 2 - (size + 10);
    		params.leftMargin = ((width - (text.length() * (size + 10))) / 2) + (i * (size + 10));
    		kString[i].invalidate();
    		this.addView(kString[i],params);
        }
	}
	/**
	 * @param steps The steps to set
	 */
	public void setSteps(List<int[][]> steps)
	{
		this.steps = steps;
	}
	/**
	 * @return The speed
	 */
	public int getSpeed()
	{
		return speed;
	}
	/**
	 * @return The animation status
	 */
	public boolean getStatus()
	{
		return finished;
	}
    /**
     * This method set speed of animation.
     * @param newSpeed Speed value to set.
     */	
	public void setSpeed(int newSpeed)
	{
		speed = newSpeed;
	}

	public int[][] sendMessage(int mes)
	{
		int[][] tmp = new int[1][mes];
		return tmp;
	}
	/**
     * This method generate animation and start animation.
     */	
	@SuppressLint("HandlerLeak")
	public void runAnimation()
	{
		handler = new Handler()
		{
			public void handleMessage(Message msg)
			{
				switch (msg.what)
				{
					case 1:
					{
						if(pause == false && finished == false)
						{
							if(position < steps.size())
							{
								int[][] a = steps.get(position);
								switch(a[0][0])
								{
									case 0:
									{
										compare(a[1][0],a[2][0]);
										break;
									}
									case 1:
									{
										skip(a[1][0]);
										break;
									}
									case 2:
									{
										clear();
										break;
									}
								}
								position++;
							}
							else
							{
								finished = true;
							}
						}
						break;
					}
				}
				super.handleMessage(msg);
			}
		};
		handler.sendEmptyMessage(1);
	}
    /**
     * This method pause animation.
     */	
	public void pauseAnimation()
	{
		pause = true;
	}
    /**
     * This method resume animation from pause position.
     */	
	public void resumeAnimation()
	{
		pause = false;
		if(!running)
		{
			handler.sendEmptyMessage(1);
		}
	}
    /**
     * This method stop animation and clear layout.
     */	
	public void stopAnimation()
	{
		finished = true;
	}
    /**
     * This method show next step of animation when animation is pause.
     */	
	public void nextStep()
	{
		if(pause == true && running == false)
		{
			if(position < steps.size())
			{
				int[][] a = steps.get(position);
				switch(a[0][0])
				{
					case 0:
					{
						compare(a[1][0],a[2][0]);
						break;
					}
					case 1:
					{
						skip(a[1][0]);
						break;
					}
					case 2:
					{
						clear();
						break;
					}
				}
				position++;
			}
		}
	}
    /**
     * This method show last step of animation when animation is pause.
     */	
	public void lastStep()
	{
		if(pause == true && running == false)
		{
			if(position > 0)
			{
				position--;
				int[][] a = steps.get(position);
				switch(a[0][0])
				{
					case 0:
					{
						compare(a[1][0],a[2][0]);
						break;
					}
					case 1:
					{
						skipb(a[1][0]);
						break;
					}
					case 2:
					{
						clear();
						break;
					}
				}
			}
		}
	}
	/**
     * This method generate animation for compare two chars.
     * @param tarPos Position of target string.
     * @param keyPos Position of keyword string.
     */
	private void compare(final int tarPos, final int keyPos)
	{
		if(text.substring(tarPos, tarPos + 1).equals(pattern.substring(keyPos, keyPos + 1)))
		{
			tString[tarPos].colorGreen();
			kString[keyPos].colorGreen();
		}
		else
		{
			tString[tarPos].colorRed();
			kString[keyPos].colorRed();
		}
		handler.sendEmptyMessageDelayed(1, speed * 150);
	}	
	/**
     * This method generate animation for clear status of animation.
     */
	private void clear()
	{
		for(int i = 0; i < tString.length; i++)
		{
			tString[i].colorBlue();
		}
		for(int i = 0; i < kString.length; i++)
		{
			kString[i].colorBlue();
		}
		handler.sendEmptyMessageDelayed(1, speed * 150);
	}	
    /**
     * This method generate animation of moving keywords.
     * @param step Step for moving keyword.
     */
	private void skip(int step)
	{
		final int keyStep = stepm * step;
		final int stepSpeed = speed * 150;
		running = true;
        for(int i = 0; i < kString.length; i++)
        {
        	final int tmp = i;
        	TranslateAnimation animation = new TranslateAnimation(0, keyStep, 0, 0);
        	animation.setDuration(stepSpeed);
        	kString[tmp].startAnimation(animation);

        	animation.setAnimationListener(new AnimationListener()
        	{
    	        public void onAnimationEnd(Animation animation)
    	        {
    	        	kString[tmp].clearAnimation();
    	        	kString[tmp].layout(kString[tmp].getLeft() + keyStep, kString[tmp].getTop(), kString[tmp].getRight() + keyStep, kString[tmp].getBottom());
    	        	if(tmp == kString.length - 1)
    	        	{
    	        		running = false;
    	        		handler.sendEmptyMessage(1);
    	        	}
    	        }

    			public void onAnimationRepeat(android.view.animation.Animation arg0) {}
    			public void onAnimationStart(android.view.animation.Animation arg0) {}
            });
        }
	}
    /**
     * This method generate animation of moving keywords.
     * @param step Step for moving keyword.
     */
	private void skipb(int step)
	{
		final int keyStep = stepm * step * -1;
		final int stepSpeed = speed * 150;
		running = true;
        for(int i = 0; i < kString.length; i++)
        {
        	final int tmp = i;
        	TranslateAnimation animation = new TranslateAnimation(0, keyStep, 0, 0);
        	animation.setDuration(stepSpeed);
        	kString[tmp].startAnimation(animation);

        	animation.setAnimationListener(new AnimationListener()
        	{
    	        public void onAnimationEnd(Animation animation)
    	        {
    	        	kString[tmp].clearAnimation();
    	        	kString[tmp].layout(kString[tmp].getLeft() + keyStep, kString[tmp].getTop(), kString[tmp].getRight() + keyStep, kString[tmp].getBottom());
    	        	if(tmp == kString.length - 1)
    	        	{
    	        		running = false;
    	        		handler.sendEmptyMessage(1);
    	        	}
    	        }

    			public void onAnimationRepeat(android.view.animation.Animation arg0) {}
    			public void onAnimationStart(android.view.animation.Animation arg0) {}
            });
        }
	}
}
